//
//  Api.swift
//  SwiftBase
//
//  Created by 郭艳波 on 2018/3/7.
//  Copyright © 2018年 郭艳波. All rights reserved.
//

import Foundation
import Alamofire
import RxSwift
import ObjectMapper

class CoreApi {
    public static let sharedInstance = CoreApi()
    public let disposeBag = DisposeBag()
    private init() {}

    let User = UserApi.sharedInstance
    let Common = CommonApi.sharedInstance

    public typealias ResponseData = Dictionary<String, Any>
    public typealias RequestCompletion = (_ data: ResponseData) -> Void

    private static let actionMap = [
        "user#user/login": ["phone", "password"],
        "user#user/logout": [],
        "user#user/register": ["phone", "password", "code"],

        "common#common/phone-verification-code-send": ["phone"],
        "common#common/phone-verification-code-check": ["phone", "code"],
    ];

    static func doRequest<T: Mappable>(action: String, data: Dictionary<String, String>?) -> Observable<T> {
        return Observable<T>.create { observer in
            let url = "\(CoreConst.NET_END_POINT)\(CoreConst.CLIENT_VERSION)\(action)"
            
            print("url: \(url)")
            print("data: \(data as Any)")
            
            Alamofire.request(url, method: .post, parameters: data).responseJSON { response in
                
                if let responseValue = response.result.value {
                    if responseValue is ResponseData {
                        let responseDict = responseValue as! CoreApi.ResponseData
                        print("responseDict: \(responseDict)")

                        let errorResponse = Mapper<ErrorResponse>().map(JSON: responseDict)
                        if errorResponse != nil && errorResponse!.code > 0 {
                            if (errorResponse!.code == 2) {
                                // token 失效， 跳转到登录页面
                                CoreData.setToken(value: "")
                                DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) {
                                    let loginNavController = UIStoryboard(name: "Login", bundle: nil).instantiateViewController(withIdentifier: "LoginNaviController")
                                    let appDelegate = UIApplication.shared.delegate as! AppDelegate
                                    appDelegate.window?.rootViewController = loginNavController
                                }
                            }
                            observer.onError(CoreApiError(code: errorResponse!.code, message: errorResponse!.message))
                            return
                        }

                        let t = Mapper<T>().map(JSON: responseDict)
                        if t != nil {
                            observer.onNext(t!)
                            return
                        }

                    }
                }

                observer.onError(CoreApiError(code: -1, message: (response.error?.localizedDescription)!, data: response as AnyObject))
            }

            return Disposables.create()
        }
    }
    
    static func uploadImage<T: Mappable>(reseponseType: T.Type, data: Data) -> Observable<T> {
        
        return Observable<T>.create { observer in
            
            let url = "\(CoreConst.IMG_UPLOAD_END_POINT)"
            print("url: \(url)")
            
            Alamofire.upload(multipartFormData: { (m) in
                let imageName = String(describing: NSDate()) + ".png"
                m.append(data, withName: "file", fileName: imageName, mimeType: "image/png")
            }, to: url) { (encodingResult) in
                switch encodingResult {
                case .success(let request, _, _):
                    request.responseJSON { response in
                        if let responseValue = response.result.value {
                            if responseValue is ResponseData {
                                let responseDict = responseValue as! CoreApi.ResponseData
                                print("responseDict: \(responseDict)")
                                
                                let errorResponse = Mapper<ErrorResponse>().map(JSON: responseDict)
                                if errorResponse != nil && errorResponse!.code > 0 {
                                    observer.onError(CoreApiError(code: errorResponse!.code, message: errorResponse!.message))
                                    return
                                }
                                
                                let t = Mapper<T>().map(JSON: responseDict)
                                if t != nil {
                                    observer.onNext(t!)
                                    return
                                }
                            }
                        }
                        
                        observer.onError(CoreApiError(code: -1, message: (response.error?.localizedDescription)!, data: response as AnyObject))
                    }
                case .failure(let error):
                    observer.onError(CoreApiError(code: -1, message: error.localizedDescription, data: nil))
                }
            }
            
            return Disposables.create()
        }
    }

    private static func makeRequest<T: Mappable>(reseponseType: T.Type, args: String ...) -> Observable<T> {
        let actionKey = args[0]
        let params = CoreApi.actionMap[actionKey]
        var data = [String: String]()
        data["token"] = CoreData.getToken()

        var i = 1
        for param: String in params! {
            data[param] = args[i]
            i += 1
        }


        let actionKeyParts = actionKey.components(separatedBy: "#")
        let action = actionKeyParts[1]

        return CoreApi.doRequest(action: action, data: data)
    }

    public class UserApi {
        public static let sharedInstance = UserApi()
        private init() {}

        public func login(phone: String, password: String) -> Observable<LoginResponse> { return CoreApi.makeRequest(reseponseType: LoginResponse.self, args: "user#user/login", phone, password) }

        public func logout() -> Observable<NoDataResponse> { return CoreApi.makeRequest(reseponseType: NoDataResponse.self, args: "user#user/logout") }

        public func register(phone: String, password: String, code: String) -> Observable<RegisterResponse> { return CoreApi.makeRequest(reseponseType: RegisterResponse.self, args: "user#user/register", phone, password, code) }
    }

    public class CommonApi {
        public static let sharedInstance = CommonApi()
        private init() {}

        public func sendPhoneVerificationCode(phone: String, countryCode: String) -> Observable<NoDataResponse> { return CoreApi.makeRequest(reseponseType: NoDataResponse.self, args: "common#common/phone-verification-code-send", phone, countryCode) }

        public func checkPhoneVerificationCode(phone: String, countryCode: String, code: String) -> Observable<NoDataResponse> { return CoreApi.makeRequest(reseponseType: NoDataResponse.self, args: "common#common/phone-verification-code-check", phone, countryCode, code) }
        
        public func uploadImage(data: Data) -> Observable<UploadFileResponse> { return CoreApi.uploadImage(reseponseType: UploadFileResponse.self, data: data) }
    }
}

protocol CoreApiErrorProtocol: Error {
    var localizedTitle: String { get }
    var localizedDescription: String { get }
    var code: Int { get }
}

struct CoreApiError: CoreApiErrorProtocol {
    var localizedTitle: String = ""
    var localizedDescription: String = ""

    var message: String
    var code: Int
    var data: AnyObject?

    init(code: Int, message: String) {
        self.init(code: code, message: message, data: nil)
    }

    init(code: Int, message: String, data: AnyObject?) {
        self.message = message
        self.code = code
        self.data = data
    }
}
